/**
 * Demo:
 * export class Resp_GR_LogonInfo extends BaseProto
 * {
 *      public wServerID       :WORD  = new WORD()
 *      public dwServerAddr    :DWORD = new DWORD()
 *      public szServerUrl     :TCHAR = new TCHAR(128) //128 字符型的需要传字符长度
   }
   //对象初始化
   let logonInfo:Resp_GR_LogonInfo = new Resp_GR_LogonInfo();
   logonInfo.read(buf);

   //对象数据的引用
   UtilTool.printLog(logonInfo.wServerID.data);
   UtilTool.printLog(logonInfo.dwServerAddr.data);
   UtilTool.printLog(logonInfo.szServerUrl.data);

   //请求写法
   export class Req_GP_RegisterVisitor_Auto extends BaseProto
    {
        cbIosGame:BYTE = new BYTE();  //BYTE		//游戏版本  203
        decode()
        {
            let bStream:ByteStream = GetByteStream.getStream(LoginConst.MDM_GP_LOGON,LoginConst.SUB_GP_REGISTER_VISITOR,1);
            this.cbIosGame.data = LoginConst.PHONE_PLAZE;
            this.write(bStream);
            return bStream.buffer;
        }
    };
    let req:Req_GP_RegisterVisitor_Auto = new Req_GP_RegisterVisitor_Auto();
    ws.send(req.decode());
 */

import { ByteStream, Define } from "../tools/ByteStream";
import UtilTool from "../tools/UtilTool";


/**BYTE 对象 */
export class BYTE
{
    public data:number;
    public read(bs:ByteStream)
    {        
        this.data = BaseTypeCheck.read(bs,BaseTypeCheck.TYPE_BYTE);
    }
    /**需要先对data赋值 */
    public write(bs:ByteStream)
    {
        BaseTypeCheck.write(bs,BaseTypeCheck.TYPE_BYTE,this.data);
    }
}
/**BOOLEAN 对象 */
export class BOOLEAN
{
    public data:boolean;
    public read(bs:ByteStream)
    {        
        this.data = BaseTypeCheck.read(bs,BaseTypeCheck.TYPE_BOOLEAN);
    }
    /**需要先对data赋值 */
    public write(bs:ByteStream)
    {
        BaseTypeCheck.write(bs,BaseTypeCheck.TYPE_BOOLEAN,this.data);
    }
}
/**WORD 对象 */
export class WORD
{
    public data:number;
    public read(bs:ByteStream)
    {        
        this.data = BaseTypeCheck.read(bs,BaseTypeCheck.TYPE_WORD);
    }
    /**需要先对data赋值 */
    public write(bs:ByteStream)
    {
        BaseTypeCheck.write(bs,BaseTypeCheck.TYPE_WORD,this.data);
    }
}
/**DWORD 对象 */
export class DWORD
{
    public data:number;
    public read(bs:ByteStream)
    {        
        this.data = BaseTypeCheck.read(bs,BaseTypeCheck.TYPE_DWORD);
    }
    /**需要先对data赋值 */
    public write(bs:ByteStream)
    {
        BaseTypeCheck.write(bs,BaseTypeCheck.TYPE_DWORD,this.data);
    }
}
/**FLOAT 对象 */
export class FLOAT
{
    public data:number;
    public read(bs:ByteStream)
    {        
        this.data = BaseTypeCheck.read(bs,BaseTypeCheck.TYPE_FLOAT);
    }
    /**需要先对data赋值 */
    public write(bs:ByteStream)
    {
        BaseTypeCheck.write(bs,BaseTypeCheck.TYPE_FLOAT,this.data);
    }
}
/**LONG 对象 */
export class LONG
{
    public data:number;
    public read(bs:ByteStream)
    {        
        this.data = BaseTypeCheck.read(bs,BaseTypeCheck.TYPE_LONG);
    }
    /**需要先对data赋值 */
    public write(bs:ByteStream)
    {
        BaseTypeCheck.write(bs,BaseTypeCheck.TYPE_LONG,this.data);
    }
}
/**DOUBLE 对象 */
export class DOUBLE
{
    public data:number;
    public read(bs:ByteStream)
    {        
        this.data = BaseTypeCheck.read(bs,BaseTypeCheck.TYPE_DOUBLE);
    }
    /**需要先对data赋值 */
    public write(bs:ByteStream)
    {
        BaseTypeCheck.write(bs,BaseTypeCheck.TYPE_DOUBLE,this.data);
    }
}
/**TCHAR 对象 */
export class TCHAR
{
    public data:string;
    public len:number=0;
    constructor(strlen: number)
    {
        this.len = strlen;
    }
    /**字符型的需要传字符长度 */
    public read(bs:ByteStream)
    {        
        this.data = BaseTypeCheck.read(bs,BaseTypeCheck.TYPE_TCHAR,false,this.len);
    }
    /**需要先对data赋值 字符型的需要传字符长度*/
    public write(bs:ByteStream)
    {
        BaseTypeCheck.write(bs,BaseTypeCheck.TYPE_TCHAR,this.data,false,this.len);
    }
}

/**Number 数组对象 包含boolean
 * arrlen 数组长度
 * ntype  字符类型 BaseTypeCheck ==> BYTE WORD DOWRD FLOAT DOUNLE LONG BOOLEAN
 */
export class NUMBERS
{
    public data:any[];
    public len:number=0;
    public numberType:string;
    /**
     * 
     * @param arrlen 数组长度，
     * @param ntype 字符类型 BYTE WORD DOWRD FLOAT DOUNLE LONG BOOLEAN
     */
    constructor(arrlen: number,ntype:string)//BaseTypeCheck.TYPE_DWORD
    {
        this.len = arrlen;
        this.numberType = ntype;
    }
    public read(bs:ByteStream)
    {      
        this.data = [];
        for(let i=0;i<this.len;i++)
        {
            if(i==0)
                this.data.push(BaseTypeCheck.read(bs,this.numberType));
            else
                this.data.push(BaseTypeCheck.read(bs,this.numberType,false));
        }  
    }
    /**需要先对data赋值 */
    public write(bs:ByteStream)
    {
        for(let i=0;i<this.len;i++)
        {
            let d = this.data[i]==(undefined||null)?0:this.data[i];
            if(i==0)
                BaseTypeCheck.write(bs,this.numberType,d);
            else
                BaseTypeCheck.write(bs,this.numberType,d,false);
        }
    }
}
/**Number 2维数组对象  包含boolean*/
export class NUMBERSS
{
    public data:any[][];
    public arrlen:number=0;
    public arrnum:number=0;
    public numberType:string;
    /**
     * arr[arrnum][arrlen]
     * @param arrnum 
     * @param arrlen 
     * @param ntype  字符类型 BYTE WORD DOWRD FLOAT DOUNLE LONG BOOLEAN
     */
    constructor(arrnum:number,arrlen: number,ntype:string)
    {
        this.arrlen = arrlen;
        this.arrnum = arrnum;
        this.numberType = ntype;
    }
    public read(bs:ByteStream)
    {      
        this.data = [];
        for(let j=0;j<this.arrnum;j++)
        {
            let idata = [];
            for(let i=0;i<this.arrlen;i++)
            {
                if(j==0&&i==0)
                    idata.push(BaseTypeCheck.read(bs,this.numberType));
                else
                    idata.push(BaseTypeCheck.read(bs,this.numberType,false));
            } 
            this.data.push(idata);
        } 
    }
    /**需要先对data赋值 */
    public write(bs:ByteStream)
    {        
        for(let j=0;j<this.arrnum;j++)
        {
            let idata = this.data[j];
            if(idata==(undefined||null))idata=[];
            for(let i=0;i<this.arrlen;i++)
            {
                let d = idata[i]==(undefined||null)?0:idata[i];
                if(j==0&&i==0)
                    BaseTypeCheck.write(bs,this.numberType,d);
                else
                    BaseTypeCheck.write(bs,this.numberType,d,false);
            }
        } 
    }
}
/**TCHAR 数组对象 */
export class TCHARS
{
    public data:string[];
    public arrlen:number=0;
    public strlen:number=0;
    /**数组长度 字符串长度 */
    constructor(arrlen: number,strlen: number)
    {
        this.arrlen = arrlen;
        this.strlen = strlen;
    }
    public read(bs:ByteStream)
    {      
        this.data = [];
        for(let i=0;i<this.arrlen;i++)
        {
            this.data.push(BaseTypeCheck.read(bs,BaseTypeCheck.TYPE_TCHAR,false,this.strlen));
        }  
    }
    /**需要先对data赋值 */
    public write(bs:ByteStream)
    {
        for(let i=0;i<this.arrlen;i++)
        {
            let d = this.data[i]==(undefined||null)?"":this.data[i];
            BaseTypeCheck.write(bs,BaseTypeCheck.TYPE_TCHAR,d,false,this.strlen);
        }
    }
}

/**TCHAR 2维数组对象 */
export class TCHARSS
{
    public data:string[][];
    public arrnum:number=0;
    public arrlen:number=0;
    public strlen:number=0;
    /**
     * arr[arrnum][arrlen]
     * @param arrnum 二维数组长度
     * @param arrlen 一维数组长度
     * @param strlen 字符串长度
     */
    constructor(arrnum:number,arrlen: number,strlen: number)
    {
        this.arrlen = arrlen;
        this.arrnum = arrnum;
        this.strlen = strlen;
    }
    public read(bs:ByteStream)
    {      
        this.data = [];
        for(let j=0;j<this.arrnum;j++)
        {
            let idata = [];
            for(let i=0;i<this.arrlen;i++)
            {
                idata.push(BaseTypeCheck.read(bs,BaseTypeCheck.TYPE_TCHAR,false,this.strlen));
            } 
            this.data.push(idata);
        } 
    }
    /**需要先对data赋值 */
    public write(bs:ByteStream)
    {        
        for(let j=0;j<this.arrnum;j++)
        {
            let idata = this.data[j];
            if(idata==(undefined||null))idata=[];
            for(let i=0;i<this.arrlen;i++)
            {
                let d = idata[i]==(undefined||null)?"":idata[i];
                BaseTypeCheck.write(bs,BaseTypeCheck.TYPE_TCHAR,idata[i],false,this.strlen);
            }
        } 
    }
}

/**嵌套子结构体一维数组 */
export class ChildProtos
{
    public data:any[];

    private arrlen:number;
    private itemClass
    /**
     * 
     * @param arrlen 数组长度
     * @param cls 嵌套结构体类名  
     */
    constructor(arrlen: number,cls)
    {
        this.arrlen = arrlen;
        this.itemClass = cls;
    }
    read(bs:ByteStream)//136
    {
        this.data = [];
        for(let i=0;i<this.arrlen;i++)
        {
            let item = new this.itemClass();
            item.read(bs);
            this.data.push(item);
        }
    }
}
/**嵌套子结构体二维数组 */
export class ChildProtoss
{
    public data:any[][];

    private arrnum:number;
    private arrlen:number;
    private itemClass
    /**
     * 
     * @param arrnum 数组长度
     * @param arrlen 数组长度
     * @param cls 嵌套结构体类名 
     */
    constructor(arrnum:number,arrlen: number,cls)
    {
        this.arrlen = arrlen;
        this.itemClass = cls;
    }
    read(bs:ByteStream)//136
    {
        this.data = [];
        for(let i=0;i<this.arrnum;i++)
        {
            let itemData = [];
            for(let j=0;j<this.arrlen;j++)
            {
                let item = new this.itemClass();
                item.read(bs);
                itemData.push(item);
            }
            this.data.push(itemData);
        }
    }
}

export class BaseProto
{
    read(buf:ArrayBuffer)
    {
        let byteStream = new ByteStream(buf);
        for (const key in this) {
            if (this.hasOwnProperty(key)) {
                let element:any = this[key];
                UtilTool.printLog(typeof(element))
                if(BaseTypeCheck.checkType(element))
                {
                    element.read(byteStream);
                    // this[key] = element.data;
                }
                else if(element instanceof BaseChildProto||element instanceof ChildProtos ||element instanceof ChildProtoss)
                {
                    element.read(byteStream);
                }
            }
        }
    }
    /**需要先对对象赋值后再调用 */
    write(bs:ByteStream)
    {
        for (const key in this) {
            if (this.hasOwnProperty(key)) {
                let element:any = this[key];
                UtilTool.printLog(typeof(element))
                if(BaseTypeCheck.checkType(element))
                    element.write(bs);
            }
        }
    }
}
/**嵌套子结构体基类 */
export class BaseChildProto
{
    read(bs:ByteStream)
    {
        for (const key in this) {
            if (this.hasOwnProperty(key)) {
                let element:any = this[key];
                UtilTool.printLog(typeof(element))
                if(BaseTypeCheck.checkType(element))
                {
                    element.read(bs);
                    // this[key] = element.data;
                }
            }
        }
    }
}

export class BaseTypeCheck
{
    static TYPE_BYTE:string="BYTE";
    static TYPE_BOOLEAN:string="BOOLEAN";
    static TYPE_WORD:string="WORD";
    static TYPE_DWORD:string="DWORD";
    static TYPE_FLOAT:string="FLOAT";
    static TYPE_LONG:string="LONG";
    static TYPE_DOUBLE:string="DOUBLE";
    static TYPE_TCHAR:string="TCHAR";
    
    //------------------------------------------
    /**数字类型一维数组 BYTE WORD DWORD LONG FLOAT DOUBLE 包含Boolean类型 */
    static TYPE_NUMBERS:string="NUMBERS";
    /**数字类型二维数组 BYTE WORD DWORD LONG FLOAT DOUBLE 包含Boolean类型 */
    static TYPE_NUMBERSS:string="NUMBERSS";//    
    static TYPE_TCHARS:string="TCHARS";//字符类型一维数组
    static TYPE_TCHARSS:string="TCHARSS";//字符类型二维数组

    private static TYPES = [BYTE,BOOLEAN,WORD,DWORD,FLOAT,LONG,DOUBLE,TCHAR,NUMBERS,NUMBERSS,TCHARS,TCHARSS];
    public static checkType(obj):boolean
    {
        let bool = false;
        BaseTypeCheck.TYPES.forEach(element => {
            if(obj instanceof element)
            {
                bool = true;
                return;
            }
        });
        return bool;
    }
    /**只处理基础类型数据 BYTE BOOLEAN WORD DWORD LONG FLOAT DOUBLE TCHAR */
    public static read(bs:ByteStream,type:string="",checkOffset:boolean=true,strlen:number=0):any
    {
        if(type == BaseTypeCheck.TYPE_BYTE)
        {
            /* if(checkOffset&&bs.Offset%Define.BYTE_LEN!=0)
            {
                bs.checkByteOffset(Define.BYTE_LEN);
            } */
            return bs.readByte();
        }
        if(type == BaseTypeCheck.TYPE_BOOLEAN)
        {
            /* if(checkOffset&&bs.Offset%Define.BYTE_LEN!=0)
            {
                bs.checkByteOffset(Define.BYTE_LEN);
            } */
            return bs.readBoolean();
        }
        else if(type == BaseTypeCheck.TYPE_WORD)
        {
            if(checkOffset&&bs.Offset%Define.SORT_LEN!=0)
            {
                bs.checkByteOffset(Define.SORT_LEN);
            }
            return bs.readUsort();
        }
        else if(type == BaseTypeCheck.TYPE_DWORD)
        {
            if(checkOffset&&bs.Offset%Define.INT_LEN!=0)
            {
                bs.checkByteOffset(Define.INT_LEN);
            }
            return bs.readUint();
        }
        else if(type == BaseTypeCheck.TYPE_FLOAT)
        {
            if(checkOffset&&bs.Offset%Define.FLOAT_LEN!=0)
            {
                bs.checkByteOffset(Define.FLOAT_LEN);
            }
            return bs.readFloat();
        }
        else if(type == BaseTypeCheck.TYPE_LONG)
        {
            if(checkOffset&&bs.Offset%Define.INT_LEN!=0)
            {
                bs.checkByteOffset(Define.INT_LEN);
            }
            return bs.readInt();
        }
        else if(type == BaseTypeCheck.TYPE_DOUBLE)
        {
            if(checkOffset&&bs.Offset%Define.DOUBLE_LEN!=0)
            {
                bs.checkByteOffset(Define.DOUBLE_LEN);
            }
            return bs.readDouble();
        }
        else if(type == BaseTypeCheck.TYPE_TCHAR)
        {
            return bs.readString(strlen);
        }
        return null;   
    }
    /**只处理基础类型数据 BYTE BOOLEAN WORD DWORD LONG FLOAT DOUBLE TCHAR */
    public static write(bs:ByteStream,type:string="",data:any,checkOffset:boolean=true,strlen:number=0)
    {
        if(type == BaseTypeCheck.TYPE_BYTE)
        {
            /* if(checkOffset&&bs.Offset%Define.BYTE_LEN!=0)
            {
                bs.checkByteOffset(Define.BYTE_LEN);
            } */
            bs.writeByte(data);
        }
        if(type == BaseTypeCheck.TYPE_BOOLEAN)
        {
            /* if(checkOffset&&bs.Offset%Define.BYTE_LEN!=0)
            {
                bs.checkByteOffset(Define.BYTE_LEN);
            } */
            bs.writeBoolean(data);
        }
        else if(type == BaseTypeCheck.TYPE_WORD)
        {
            if(checkOffset&&bs.Offset%Define.SORT_LEN!=0)
            {
                bs.checkByteOffset(Define.SORT_LEN);
            }
            bs.writeUSort(data);
        }
        else if(type == BaseTypeCheck.TYPE_DWORD)
        {
            if(checkOffset&&bs.Offset%Define.INT_LEN!=0)
            {
                bs.checkByteOffset(Define.INT_LEN);
            }
            bs.writeUint(data);
        }
        else if(type == BaseTypeCheck.TYPE_FLOAT)
        {
            if(checkOffset&&bs.Offset%Define.FLOAT_LEN!=0)
            {
                bs.checkByteOffset(Define.FLOAT_LEN);
            }
            bs.writeFloat(data);
        }
        else if(type == BaseTypeCheck.TYPE_LONG)
        {
            if(checkOffset&&bs.Offset%Define.INT_LEN!=0)
            {
                bs.checkByteOffset(Define.INT_LEN);
            }
            bs.writeInt(data);
        }  
        else if(type == BaseTypeCheck.TYPE_DOUBLE)
        {
            if(checkOffset&&bs.Offset%Define.DOUBLE_LEN!=0)
            {
                bs.checkByteOffset(Define.DOUBLE_LEN);
            }
            bs.writeDouble(data);
        }  
        else if(type == BaseTypeCheck.TYPE_TCHAR)
        {
            bs.writeString(data,strlen);
        } 
    }
}